﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk;
using System.Web.Services.Protocols;
using System.Xml;
using Microsoft.Crm.SdkTypeProxy.Metadata;
using Microsoft.Crm.Sdk.Metadata;
using System.ComponentModel;
using Streamsol.Crm;
using System.Reflection;
using Streamsol.Helpers;
using System.Xml.Linq;
using Streamsol.Crm.XrmDevelopmentToolkit;
using System.IO;

namespace Streamsol.Crm
{
    public static class CrmServiceHelper
    {
        /// <summary>
        /// Retrieves a BusinessEntity from the CrmService.
        /// </summary>
        /// <typeparam name="TEntity">The entity type</typeparam>
        /// <param name="service"></param>
        /// <param name="entityId">The Guid of the entity you wish to receive</param>
        /// <param name="columns">A string array of columns to retrieve</param>
        /// <returns>A BusinessEntity</returns>
        public static TEntity Retrieve<TEntity>(this CrmService service, Guid entityId, params string[] columns)
            where TEntity : BusinessEntity, new()
        {
            return (TEntity)service.Retrieve(typeof(TEntity).Name, entityId, new ColumnSet(columns));
        }

        /// <summary>
        /// Tries to retrieve a BusinessEntity from the CrmService, if not found it will return null.
        /// </summary>
        /// <typeparam name="TEntity">The entity type</typeparam>
        /// <param name="service"></param>
        /// <param name="entityId">The Guid of the entity you wish to receive</param>
        /// <returns>A BusinessEntity</returns>
        public static TEntity TryRetrieve<TEntity>(this CrmService service, Guid entityId)
            where TEntity : BusinessEntity, new()
        {
            return service.TryRetrieve<TEntity>(entityId, null);
        }

        /// <summary>
        /// Tries to retrieve a BusinessEntity from the CrmService, if not found it will return null.
        /// </summary>
        /// <typeparam name="TEntity">The entity type</typeparam>
        /// <param name="service"></param>
        /// <param name="entityId">The Guid of the entity you wish to receive</param>
        /// <returns>A BusinessEntity</returns>
        public static TEntity TryRetrieve<TEntity>(this CrmService service, Guid entityId, params string[] columns)
            where TEntity : BusinessEntity, new()
        {
            string entityName = typeof(TEntity).Name;

            try
            {
                TEntity entity = (TEntity)service.Retrieve(entityName, entityId, BuildColumnSet(columns));
                return entity;
            }
            catch (SoapException sex)
            {
                if (sex.Detail.GetErrorCode() == "0x80040217")
                    return null;
                else throw;
            }
        }

        /// <summary>
        /// Not a practical method. Just an example.
        /// </summary>
        /// <typeparam name="TEntity">The entity type</typeparam>
        /// <param name="service"></param>
        /// <returns>An IEnumerable collection of BusinessEntity objects</returns>
        public static IEnumerable<TEntity> RetrieveAllActive<TEntity>(this CrmService service)
            where TEntity : BusinessEntity, new()
        {
            return service.RetrieveAllActive<TEntity>(null);
        }

        /// <summary>
        /// Not a practical method. Just an example.
        /// </summary>
        /// <typeparam name="TEntity">The entity type</typeparam>
        /// <param name="service"></param>
        /// <returns>An IEnumerable collection of BusinessEntity objects</returns>
        public static IEnumerable<TEntity> RetrieveAllActive<TEntity>(this CrmService service, params string[] columns)
            where TEntity : BusinessEntity, new()
        {
            QueryExpression qe = new QueryExpression()
            {
                ColumnSet = BuildColumnSet(columns),
                EntityName = typeof(TEntity).Name,
                Criteria = new FilterExpression()
            };

            qe.Criteria.Conditions.Add(new ConditionExpression("statecode", ConditionOperator.Equal, new object[] { 0 }));

            return service.RetrieveMultiple(qe).BusinessEntities.Cast<TEntity>();
        }

        /// <summary>
        /// Not a practical method. Just an example.
        /// </summary>
        /// <typeparam name="TEntity">The entity type</typeparam>
        /// <param name="service"></param>
        /// <returns>An IEnumerable collection of BusinessEntity objects</returns>
        public static IEnumerable<BusinessEntity> RetrieveAllActive(this CrmService service, string entityName, params string[] columns)
        {
            QueryExpression qe = new QueryExpression()
            {
                ColumnSet = BuildColumnSet(columns),
                EntityName = entityName,
                Criteria = new FilterExpression()
            };

            qe.Criteria.Conditions.Add(new ConditionExpression("statecode", ConditionOperator.Equal, new object[] { 0 }));

            return ((RetrieveMultipleResponse)service.Execute(new RetrieveMultipleRequest()
            {
                Query = qe,
                ReturnDynamicEntities = true
            })).BusinessEntityCollection.BusinessEntities.AsEnumerable();
        }

        /// <summary>
        /// Creates and Retrieves a BusinessEntity
        /// </summary>
        /// <typeparam name="TEntity">The type of BusinessEntity</typeparam>
        /// <param name="service"></param>
        /// <param name="entity">The entity</param>
        /// <returns>The Entity created</returns>
        public static TEntity CreateAndRetrieve<TEntity>(this CrmService service, TEntity entity)
            where TEntity : BusinessEntity, new()
        {
            return (TEntity)service.Retrieve(typeof(TEntity).Name, service.Create(entity), new AllColumns());
        }


        /// <summary>
        /// Retrieves an entity as a dynamic entity.
        /// </summary>
        /// <param name="_service">The Crm Service</param>
        /// <param name="entityName">The name of the entity you wish to retrieve</param>
        /// <param name="entityId">The GUID of the entity.</param>
        /// <returns>A dynamic entity.</returns>
        public static DynamicEntity RetrieveDynamic(this CrmService service, string entityName, Guid entityId)
        {
            return service.RetrieveDynamic(entityName, entityId, null);
        }

        /// <summary>
        /// Retrieves an entity as a dynamic entity.
        /// </summary>
        /// <param name="service">The Crm Service</param>
        /// <param name="entityName">The name of the entity you wish to retrieve</param>
        /// <param name="entityId">The GUID of the entity.</param>
        /// <param name="columns">The columns you wish to retrieve.</param>
        /// <returns>A dynamic entity.</returns>
        public static DynamicEntity RetrieveDynamic(this CrmService service, string entityName, Guid entityId, params string[] columns)
        {
            RetrieveRequest req = new RetrieveRequest()
            {
                ColumnSet = BuildColumnSet(columns),
                ReturnDynamicEntities = true,
                Target = new TargetRetrieveDynamic()
                {
                    EntityId = entityId,
                    EntityName = entityName
                }
            };

            req.ColumnSet.AddColumns(columns);

            return (DynamicEntity)((RetrieveResponse)service.Execute(req)).BusinessEntity;
        }

        /// <summary>
        /// Retrieves a BusinessEntity from the CrmService.
        /// </summary>
        /// <typeparam name="TEntity">The entity type</typeparam>
        /// <param name="service"></param>
        /// <param name="entityId">The Guid of the entity you wish to receive</param>
        /// <returns>A <typeparamref name="TEntity"/> object.</returns>
        public static TEntity Retrieve<TEntity>(this CrmService service, Guid entityId)
            where TEntity : class, new()
        {
            TEntity entity = new TEntity();

            IXrmEntity xrmEntity = entity as IXrmEntity;

            if (xrmEntity != null)
            {
                Dictionary<string, PropertyInfo> attributes = XrmHelpers.GetXrmMappedFields(xrmEntity);

                DynamicEntity de = service.RetrieveDynamic(xrmEntity.GetCrmEntityName(), entityId, attributes.Select(s => s.Key).ToArray());

                return (TEntity)de.AsXrmEntity(xrmEntity, attributes);
            }

            BusinessEntity be = entity as BusinessEntity;

            if (be != null)
            {
                return service.Retrieve(typeof(TEntity).Name, entityId, new AllColumns()) as TEntity;
            }

            throw new NotSupportedException(string.Format("The CRM Retrieve method does not support {0}", typeof(TEntity).Name));
        }

        /// <summary>
        /// Retrieves Many-to-Many records from CRM. This method is using ICrmService.
        /// </summary>
        /// <typeparam name="T">Type of the entity to return.</typeparam>
        /// <param name="sdk">Instance of the CRM web service.</param>
        /// <param name="intersectEntityName">Schema name of the intersect entity.</param>
        /// <param name="parentKey">Schema name of the first key field.</param>
        /// <param name="childKey">Schema name of the second key field.</param>
        /// <param name="childEntityName">Schema name of T.</param>
        /// <param name="key">Value of the key.</param>
        /// <returns>An IEnumerable sequence of T.</returns>
        /// <example>
        /// <para>
        /// Retrieve a list of users belonging to a specific role.
        /// </para>
        /// <code>
        /// <![CDATA[
        /// ICrmService sdk = GetICrmService();
        /// try
        /// {
        ///     Guid roleId = Guid.Empty;
        ///     List<User> users = CrmDataAccess.RetrieveManyToMany<User>(this.Sdk, "systemuserroles", "roleid", "systemuserid", "systemuser", roleId);
        ///     foreach (var user in users)
        ///     {
        ///         Console.WriteLine("User: {0}", user.FullName);
        ///     }
        /// }
        /// catch (CrmException ce)
        /// {
        ///     Console.WriteLine(ce.Message);
        /// }
        /// ]]>
        /// </code>
        /// </example>
        /// /// <example>
        /// <para>
        /// Retrieve a list of roles that a specific user has.
        /// </para>
        /// <code>
        /// <![CDATA[
        /// ICrmService sdk = GetICrmService();
        /// try
        /// {
        ///     Guid userId = Guid.Empty;
        ///     CrmDataAccess.RetrieveManyToMany<Role>(this.Sdk, "systemuserroles", "systemuserid", "roleid", "role", userId);
        ///     foreach (var role in roles)
        ///     {
        ///         Console.WriteLine("Role: {0}", role.Name);
        ///     }
        /// }
        /// catch (CrmException ce)
        /// {
        ///     Console.WriteLine(ce.Message);
        /// }
        /// ]]>
        /// </code>
        /// </example>
        /// <exception cref="CrmException">When the call to sdk.Execute() fails.</exception>
        public static IEnumerable<T> RetrieveManyToMany<T>(this CrmService service, string intersectEntityName, string parentKey, string childKey, Guid key)
            where T : BusinessEntity, new()
        {
            string fetchXml = string.Format("<fetch mapping=\"logical\"><entity name=\"{0}\"><all-attributes /><filter type=\"and\"><condition attribute=\"{1}\" operator=\"eq\" value=\"{2}\" /></filter></entity></fetch>", intersectEntityName, parentKey, key);
            string result = "";

            result = service.Fetch(fetchXml);

            if (!string.IsNullOrEmpty(result))
            {
                XDocument xdoc = XDocument.Parse(result);
                var rows = (from data in xdoc.Descendants("resultset").Descendants("result")
                            select new Guid(data.Element(childKey).Value)).ToList();

                if (rows.Count > 0)
                {
                    QueryExpression query = new QueryExpression();

                    query.EntityName = typeof(T).Name;
                    query.ColumnSet = new AllColumns();

                    query.Criteria = new FilterExpression();
                    query.Criteria.FilterOperator = LogicalOperator.And;

                    ConditionExpression condition1 = new ConditionExpression();
                    condition1.AttributeName = childKey;
                    condition1.Operator = ConditionOperator.In;
                    condition1.Values = rows.Cast<object>().ToArray();

                    query.Criteria.AddCondition(condition1);

                    RetrieveMultipleRequest rmr = new RetrieveMultipleRequest();
                    rmr.Query = query;

                    RetrieveMultipleResponse rmresponse = null;

                    rmresponse = service.Execute(rmr) as RetrieveMultipleResponse;

                    foreach (T entity in rmresponse.BusinessEntityCollection.BusinessEntities)
                    {
                        yield return entity;
                    }
                }
            }
        }

        public static Guid GetCrmGuid(this BusinessEntity entity)
        {
            return entity.ExtractPropertyValue<Key>(entity.GetPrimaryIdAttributeName()).Value;
        }

        public static Lookup CreateLookup(this BusinessEntity entity)
        {
            return new Lookup(entity.GetType().Name, entity.GetCrmGuid());
        }

        /// <summary>
        /// Performs a WhoAmIRequest
        /// </summary>
        /// <param name="service"></param>
        /// <returns>A WhoAmIResponse</returns>
        public static WhoAmIResponse WhoAmI(this CrmService service)
        {
            return ((WhoAmIResponse)service.Execute(new WhoAmIRequest()));
        }

        /// <summary>
        /// Returns the Guid of the logged in user.
        /// </summary>
        /// <param name="service"></param>
        /// <returns>The logged in CRM User Id</returns>
        public static Guid GetCurrentUserId(this CrmService service)
        {
            return service.CrmAuthenticationTokenValue.CallerId;
        }

        /// <summary>
        /// Sends and creates (if required) an email and tracks it in CRM.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="subject">The subject of the email.</param>
        /// <param name="body">The message body of the email.</param>
        /// <param name="from">A lookup of the systemuser or queue the email is from.</param>
        /// <param name="to">The receipients.</param>
        public static void SendCrmEmail(this CrmService service, string subject, string body, Lookup from, params Lookup[] to)
        {           
            email email = new email();
            email.subject = subject;
            email.description = body;
            email.from = new activityparty[] { new activityparty() { partyid = from } };

            activityparty[] recipients = new activityparty[to.Length];

            for (int i = 0; i < to.Length; i++)
            {
                recipients[i] = new activityparty() { partyid = to[i] };
            }

            email.to = recipients;

            service.SendCrmEmail(email);
        }

        /// <summary>
        /// Sends and creates (if required) an email and tracks it in CRM.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="email">The email to Send</param>
        public static void SendCrmEmail(this CrmService service, email email)
        {
            Guid emailId;

            if (email.activityid == null || email.activityid.Value == Guid.Empty)
                emailId = service.Create(email);
            else emailId = email.activityid.Value;

            SendEmailRequest req = new SendEmailRequest()
            {
                EmailId = emailId,
                IssueSend = true,
                TrackingToken = ""
            };

            service.Execute(req);
        }

        /// <summary>
        /// Issues a SetStateRequest on the entity.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="entity">The entity to change state.</param>
        /// <param name="status">The new status.</param>
        public static void ChangeStatus(this CrmService service, IXrmEntity entity, int status)
        {
            service.ChangeStatus(entity.GetCrmEntityName(), entity.Id, status, true);
        }

        /// <summary>
        /// Issues a SetStateRequest on the entity.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="entity">The entity to change state.</param>
        /// <param name="status">The new status.</param>
        /// <param name="isActiveState">If this status is an active or inactive (default is active).</param>
        public static void ChangeStatus(this CrmService service, IXrmEntity entity, int status, bool isActiveState)
        {
            service.ChangeStatus(entity.GetCrmEntityName(), entity.Id, status, isActiveState);
        }

        /// <summary>
        /// Issues a SetStateRequest on the entity.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="entity">The entity to change state.</param>
        /// <param name="status">The new status</param>
        public static void ChangeStatus(this CrmService service, BusinessEntity entity, int status)
        {
            string entityName = entity.GetType().Name;
            service.ChangeStatus(entityName, entity.ExtractPropertyValue<Guid>(entity.GetPrimaryIdAttributeName()), status, true);
        }

        /// <summary>
        /// Issues a SetStateRequest on the entity.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="entity">The entity to change state.</param>
        /// <param name="status">The new status</param>
        /// <param name="isActiveState">If this status is an active or inactive (default is active).</param>
        public static void ChangeStatus(this CrmService service, BusinessEntity entity, int status, bool isActiveState)
        {
            string entityName = entity.GetType().Name;
            service.ChangeStatus(entityName, entity.ExtractPropertyValue<Guid>(entity.GetPrimaryIdAttributeName()), status, isActiveState);
        }


        /// <summary>
        /// Issues a SetStateRequest.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="entityName">The name of the entity.</param>
        /// <param name="entityId">The Id of the entity.</param>
        /// <param name="status">The new status</param>
        public static void ChangeStatus(this CrmService service, string entityName, Guid entityId, int status)
        {
            service.ChangeStatus(entityName, entityId, status, true);
        }


        /// <summary>
        /// Issues a SetStateRequest.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="entityName">The name of the entity.</param>
        /// <param name="entityId">The Id of the entity.</param>
        /// <param name="status">The new status</param>
        /// <param name="isActiveState">If this status is an active or inactive (default is active).</param>
        public static void ChangeStatus(this CrmService service, string entityName, Guid entityId, int status, bool isActiveState)
        {
            SetStateDynamicEntityRequest req = new SetStateDynamicEntityRequest();
            req.Entity = new Moniker(entityName, entityId);
            req.State = isActiveState ? "active" : "inactive";
            req.Status = status;
            service.Execute(req);
        }

        /// <summary>
        /// Assigns the entity to the current logged in user.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="entity">The entity you wish to assign.</param>
        public static void Assign(this CrmService service, IXrmEntity entity)
        {
            service.Assign(entity, service.GetCurrentUserId());
        }

        /// <summary>
        /// Assigns the entity to the current logged in user.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="entity">The entity you wish to assign.</param>
        public static void Assign(this CrmService service, BusinessEntity entity)
        {
            service.Assign(entity, service.GetCurrentUserId());
        }

        /// <summary>
        /// Assigns the entity to the current logged in user.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="entityName">The name of the entity you wish to assign.</param>
        /// <param name="entityId">The Id of the entity you wish to assign.</param>
        public static void Assign(this CrmService service, string entityName, Guid entityId)
        {
            service.Assign(entityName, entityId, service.GetCurrentUserId());
        }

        /// <summary>
        /// Assigns the entity to a specified user.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="entity">The entity you wish to assign.</param>
        /// <param name="userId">The Id of the systemuser you wish to assign the entity to.</param>
        public static void Assign(this CrmService service, IXrmEntity entity, Guid userId)
        {
            service.Assign(entity.GetCrmEntityName(), entity.Id, userId);
        }

        /// <summary>
        /// Assigns the entity to a specified user.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="entity">The entity you wish to assign.</param>
        /// <param name="userId">The Id of the systemuser you wish to assign the entity to.</param>
        public static void Assign(this CrmService service, BusinessEntity entity, Guid userId)
        {
            //TODO: Add support for dynamic entity
            if (entity is DynamicEntity)
                throw new NotSupportedException();

            service.Assign(entity.GetType().Name, entity.GetCrmGuid(), userId);
        }

        /// <summary>
        /// Assigns the entity to a specified user.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="entityName">The name of the entity you wish to assign.</param>
        /// <param name="entityId">The Id of the entity you wish to assign.</param>
        /// <param name="userId">The Id of the systemuser you wish to assign the entity to.</param>
        public static void Assign(this CrmService service, string entityName, Guid entityId, Guid userId)
        {
            service.Execute(new AssignRequest()
            {
                Assignee = new SecurityPrincipal() { PrincipalId = userId, Type = SecurityPrincipalType.User },
                Target = new TargetOwnedDynamic() { EntityName = entityName, EntityId = entityId }
            });
        }

        /// <summary>
        /// Retrieves a list of roles for the current user.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="userId">The user id</param>
        /// <returns>A list of roles</returns>
        public static IEnumerable<role> GetUserRoles(this CrmService service)
        {
            return service.GetUserRoles(service.GetCurrentUserId());
        }

        /// <summary>
        /// Retrieves a list of roles for a given user.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="userId">The user id</param>
        /// <returns>A list of roles</returns>
        public static IEnumerable<role> GetUserRoles(this CrmService service, Guid userId)
        {
            QueryExpression qe = new QueryExpression("role");

            LinkEntity le = new LinkEntity("role", "systemuserroles", "roleid", "roleid", JoinOperator.Inner);

            LinkEntity le2 = new LinkEntity("systemuserroles", "systemuser", "systemuser", "systemuser", JoinOperator.Inner);

            ConditionExpression ce = new ConditionExpression("systemuserid", ConditionOperator.Equal, service.CrmAuthenticationTokenValue.CallerId);

            le2.LinkCriteria = new FilterExpression();
            le2.LinkCriteria.Conditions.Add(ce);

            le.LinkEntities.Add(le2);
            qe.LinkEntities.Add(le);

            return service.RetrieveMultiple(qe).BusinessEntities.Cast<role>();
        }

        /// <summary>
        /// Checks to see whether the specified user is in a specified role.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="roleName">The name of the role. (Case insensitive)</param>
        /// <param name="userId">The user id</param>
        /// <returns></returns>
        public static bool IsUserInRole(this CrmService service, string roleName)
        {
            return service.IsUserInRole(roleName, service.GetCurrentUserId());
        }

        /// <summary>
        /// Checks to see whether the specified user is in a specified role.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="roleName">The name of the role. (Case insensitive)</param>
        /// <param name="userId">The user id</param>
        /// <returns></returns>
        public static bool IsUserInRole(this CrmService service, string roleName, Guid userId)
        {
            foreach (var role in service.GetUserRoles(userId))
            {
                if (role.name.Equals(roleName, StringComparison.CurrentCultureIgnoreCase))
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Creates and attachs a note to an entity in CRM.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="subject">The subject of the note.</param>
        /// <param name="text">The body of the note.</param>
        /// <param name="entity">The entity to attach the note to.</param>
        /// <returns>The Guid of the note created.</returns>
        public static Guid CreateAnnotation(this CrmService service, string subject, string text, BusinessEntity entity)
        {
            Lookup entityLookup = entity.CreateLookup();
            return service.CreateAnnotation(subject, text, entityLookup.type, entityLookup.Value);
        }

        /// <summary>
        /// Creates and attachs a note to an entity in CRM.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="subject">The subject of the note.</param>
        /// <param name="text">The body of the note.</param>
        /// <param name="entity">The entity to attach the note to.</param>
        /// <returns>The Guid of the note created.</returns>
        public static Guid CreateAnnotation(this CrmService service, string subject, string text, IXrmEntity entity)
        {
            return service.CreateAnnotation(subject, text, entity.GetCrmEntityName(), entity.Id);
        }

        /// <summary>
        /// Creates and attachs a note to an entity in CRM.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="subject">The subject of the note.</param>
        /// <param name="text">The body of the note.</param>
        /// <param name="entityName">The name of the entity to attach the note to.</param>
        /// <param name="entityId">The GUID of the entity to attach the note to.</param>
        /// <returns>The Guid of the note created.</returns>
        public static Guid CreateAnnotation(this CrmService service, string subject, string text, string entityName, Guid entityId)
        {
            return service.Create(new annotation()
             {
                 subject = subject,
                 notetext = text,
                 objectid = new Lookup(entityName, entityId),
                 isdocument = new CrmBoolean(false)
             });

        }

        /// <summary>
        /// Creates and attaches a note with a file attachment to an entity in CRM.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="subject">The subject of the note.</param>
        /// <param name="fileName">The file name to attach.</param>
        /// <param name="fileMimeType">The MIME type of the file to attach.</param>
        /// <param name="entity">The entity to attach the note to.</param>
        /// <returns>The Guid of the note created.</returns>
        public static Guid CreateAnnotation(this CrmService service, string subject, string fileName, string fileMimeType, BusinessEntity entity)
        {
            Lookup entityLookup = entity.CreateLookup();
            return service.CreateAnnotation(subject, fileName, fileMimeType, entityLookup.type, entityLookup.Value);
        }

        /// <summary>
        /// Creates and attaches a note with a file attachment to an entity in CRM.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="subject">The subject of the note.</param>
        /// <param name="fileName">The file name to attach.</param>
        /// <param name="fileMimeType">The MIME type of the file to attach.</param>
        /// <param name="entity">The entity to attach the note to.</param>
        /// <returns>The Guid of the note created.</returns>
        public static Guid CreateAnnotation(this CrmService service, string subject, string fileName, string fileMimeType, IXrmEntity entity)
        {
            return service.CreateAnnotation(subject, fileName, fileMimeType, entity.GetCrmEntityName(), entity.Id);
        }

        /// <summary>
        /// Creates and attaches a note to an entity in CRM.
        /// </summary>
        /// <param name="service"></param>
        /// <param name="subject">The subject of the note.</param>
        /// <param name="fileName">The file name to attach.</param>
        /// <param name="fileMimeType">The MIME type of the file to attach.</param>
        /// <param name="entityName">The name of the entity to attach the note to.</param>
        /// <param name="entityId">The GUID of the entity to attach the note to.</param>
        /// <returns>The Guid of the note created.</returns>
        public static Guid CreateAnnotation(this CrmService service, string subject, string fileName, string fileMimeType, string entityName, Guid entityId)
        {
            FileInfo fi = new FileInfo(fileName);
            FileStream stream = fi.OpenRead();
            byte[] byteData = new byte[(int)stream.Length];
            stream.Read(byteData, 0, (int)stream.Length);
            string encodedData = System.Convert.ToBase64String(byteData);

            return service.Create(new annotation()
            {
                subject = subject,
                filename = fi.Name,
                mimetype = fileMimeType,
                documentbody = encodedData,
                objectid = new Lookup(entityName, entityId),
                isdocument = new CrmBoolean(false)
            });
        }

        private static ColumnSetBase BuildColumnSet(string[] columns)
        {
            ColumnSetBase columnSet = null;

            if (columns == null)
                columnSet = new AllColumns();
            else
                columnSet = new ColumnSet(columns);

            return columnSet;
        }
    }
}
